from common.object import (
    CTickData,
    CBarData,
    TradeData,
    OrderData,
    Status, Direction, Offset,
    CArbBarData,
)
from strategies.base import StopOrder, SpreadOrder
from strategies.template import CtaTemplate
import pandas as pd
import numpy as np
import datetime as dt
import math
import copy
from Engine.engine import BacktestingEngine
import operator
import types


class SpreadStrategyParabola(CtaTemplate):
    """"""

    author = "Jingpyu"

    ma_length = 300
    grid_levels = [2]
    stoploss_ratio = -1
    ma_type = "linear"

    diff_df = None
    last_grid_time = None
    trend = False
    pr_ratio_threshold = 2

    stoploss_ma1 = 5
    stoploss_ma2 = 10

    parameters = ["ma_length", "grid_levels", "spread_grid_std_multi", "min_grid_step", "slope_adjust_ratio", "stoploss_ratio", "ma_type" ]
    variables = ["diff_df", "last_grid_time"]

    def __init__(self, cta_engine, strategy_name, vt_symbols, setting):
        """"""
        if len(vt_symbols) != 2:
            raise RuntimeError("Spread Strategy only accepts two symbol as parameters")

        super(SpreadStrategyParabola, self).__init__(
            cta_engine, strategy_name, vt_symbols, setting
        )
        self.setting = setting
        self.ma = 0
        self.slope = 0
        self.atr = 0
        self.long_grids = [-999999] * len(self.grid_levels)
        self.short_grids = [999999] * len(self.grid_levels)
        self.long_mids = [0] * len(self.grid_levels)
        self.short_mids = [0] * len(self.grid_levels)
        self.long_holds = [0] * len(self.grid_levels)
        self.short_holds = [0] * len(self.grid_levels)
        self.long_hold_price = [0] * len(self.grid_levels)
        self.short_hold_price = [0] * len(self.grid_levels)
        self.long_peak_price = [-999999] * len(self.grid_levels)
        self.short_peak_price = [999999] * len(self.grid_levels)
        self.long_open_time = [None] * len(self.grid_levels)
        self.short_open_time = [None] * len(self.grid_levels)
        self.tick = self.cta_engine.pricetick
        self.long_grids_open_orders = [None] * len(self.grid_levels)
        self.long_grids_close_orders = [None] * len(self.grid_levels)
        self.short_grids_open_orders = [None] * len(self.grid_levels)
        self.short_grids_close_orders = [None] * len(self.grid_levels)
        self.daily_df = None
        self.in_stop_loss = False
        self.last_tick_data = dict()
        self.pr_ratio = 0
        self.reg_times_long = 0
        self.reg_times_short = 0
        self.cross_height_long = 0
        self.cross_height_short = 0

        self.order_changed = False
        self.xTx_1_x_w = None
        self.diff_df_index = -1
        self.diff_df_len = -1

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("策略初始化")

        if "start_date" in self.setting:
            self.load_bar(self.setting["start_date"])

        if self.stoploss_ratio >= 0:
            setting = copy.deepcopy(self.setting)
            setting["stoploss_ratio"] = -1
            setting["grid_stoploss_ratio"] = -1
            key_setting = copy.deepcopy(setting)
            key_setting["start"] = self.cta_engine.start.strftime("%Y%m%d")
            key_setting["end"] = self.cta_engine.end.strftime("%Y%m%d")
            key_setting["trading_start"] = self.cta_engine.trading_start_date
            key_setting["symbol1"] = self.vt_symbols[0]
            key_setting["symbol2"] = self.vt_symbols[1]
            key = "spread_strategy_parabola_non_stoploss_result_" + str(key_setting)
            with self.cta_engine.lock_cache(key):
                self.daily_df = self.cta_engine.fetch_cache(key)
                if self.daily_df is None:
                    # 先跑一遍没有止损的
                    engine = BacktestingEngine()
                    engine.set_parameters(
                        vt_symbols=self.cta_engine.vt_symbols,
                        interval=self.cta_engine.interval,
                        start=self.cta_engine.start,
                        end=self.cta_engine.end,
                        rate=self.cta_engine.rate,
                        slippage=self.cta_engine.slippage,
                        size=self.cta_engine.size,
                        pricetick=self.cta_engine.pricetick,
                        capital=self.cta_engine.capital,
                        mode=self.cta_engine.mode
                    )
                    engine.add_strategy(SpreadStrategyParabola, setting)

                    engine.set_start_date(dt.datetime.strptime(str(self.cta_engine.trading_start_date), "%Y%m%d").date() - dt.timedelta(days=30))
                    
                    engine.copy_data(self.cta_engine)
                    engine.run_backtesting()
                    self.daily_df = engine.calculate_result()
                    del engine
                    if self.daily_df is not None:
                        self.daily_df.index = self.daily_df.index.strftime("%Y%m%d").astype("int")
                        self.daily_df["net_pnl"] = self.daily_df["net_pnl"].cumsum()
                        self.daily_df["net_pnl_ma10"] = self.daily_df["net_pnl"].rolling(self.stoploss_ma1).mean()
                        self.daily_df["net_pnl_ma20"] = self.daily_df["net_pnl"].rolling(self.stoploss_ma2).mean()
                        self.cta_engine.output("计算完无止损的结果")
                    else:
                        self.stoploss_ratio = -1
                        self.grid_stoploss_ratio = -1
                    self.cta_engine.store_cache(key, self.daily_df, expire=30000)
                else:
                    self.cta_engine.output("从历史记录读取无止损的结果")

    def polyfit_parabola(self, y):
        weights = np.arange(len(y), len(y) * 2, 1)
        ret = np.polynomial.polynomial.polyfit(np.arange(len(y)) + 1, y, 2, w=weights)
        return len(y)*len(y) * ret[2] + len(y) * ret[1] + ret[0]



    def polyfit_linear(self, x):
        weights = np.arange(len(x), len(x) * 2, 1)
        ret = np.polynomial.polynomial.polyfit(np.arange(0, len(x), 1), x, 1, w=weights)
        return (len(x) - 1) * ret[1] + ret[0]

    def polyfit_slope(self, x):
        weights = np.arange(len(x), len(x) * 2, 1)
        ret = np.polynomial.polynomial.polyfit(np.arange(0, len(x), 1), x, 1, w=weights)
        return ret[1]

    def calculate_price_efficiency_ratio(self, df, ma_name):
        swing_locs = []
        swing_prices = []
        direc = 1
        swing_loc = 0
        swing_price = None
        index = -1
        pr_list = []
        cross_reg_times_long = []
        cross_reg_times_short = []
        cross_height_long = []
        cross_height_short = []
        atr_ratio = 0.2
        for _, row in df.iterrows():
            index += 1
            pr_list.append(0)
            cross_reg_times_long.append(0)
            cross_reg_times_short.append(0)
            cross_height_long.append(0)
            cross_height_short.append(0)
            if np.isnan(row[ma_name]) or np.isnan(row['atr']):
                continue
            if swing_price is None:
                swing_price = row["diff"]
            if direc == 1:
                if row["diff"] < row[ma_name] - atr_ratio * row["atr"]:
                    direc = -1
                    swing_locs.append(swing_loc)
                    swing_prices.append(swing_price)
                    swing_price = row["diff"]
                    swing_loc = index
                elif row["diff"] > swing_price:
                    swing_price = row["diff"]
                    swing_loc = index
            else:
                if row["diff"] > row[ma_name] + atr_ratio * row["atr"]:
                    direc = 1
                    swing_locs.append(swing_loc)
                    swing_prices.append(swing_price)
                    swing_price = row["diff"]
                    swing_loc = index
                elif row["diff"] < swing_price:
                    swing_price = row["diff"]
                    swing_loc = index

            if index > self.ma_length:
                part_swing_prices = [ y for x,y in zip(swing_locs, swing_prices) if x > (index - self.ma_length)]
                if len(part_swing_prices) < 2:
                    continue
                part_wave_heights = np.diff(np.array(part_swing_prices))
                pr_list[-1] = np.sum(np.abs(part_wave_heights))/ (np.max(part_swing_prices) - np.min(part_swing_prices))
                cross_reg_times_long[-1] = np.sum(part_wave_heights > 0)
                cross_reg_times_short[-1] = np.sum(part_wave_heights < 0)
                if cross_reg_times_long[-1] > 0:
                    cross_height_long[-1] = np.mean(part_wave_heights[part_wave_heights > 0])
                if cross_reg_times_short[-1] > 0:
                    cross_height_short[-1] = np.mean(part_wave_heights[part_wave_heights < 0])
        
        return pr_list, cross_reg_times_long, cross_reg_times_short, cross_height_long, cross_height_short


    def pre_caculate_grid(self, ma_length):
        key = "spreadstrategy_parabola_" + self.vt_symbols[0] + "_" + self.vt_symbols[1] + "_" + self.cta_engine.start.strftime("%Y%m%d") + "_" + self.cta_engine.end.strftime("%Y%m%d") + "_" + str(ma_length) + "_pregrid"
        with self.cta_engine.lock_cache(key):
            data = self.cta_engine.fetch_cache(key)
            if data is None:
                klines1 = self.load_history_data(self.vt_symbols[0], all=True)
                klines2 = self.load_history_data(self.vt_symbols[1], all=True)
                if len(klines1) != len(klines2):
                    raise RuntimeError("two kline length not match")

                dt_array = [x.timestamp for x in klines1]
                diff_array = [x.close_price - y.close_price for (x, y) in zip(klines1, klines2)]

                short_ma_length = ma_length//2
                long_ma_length = int(ma_length * 1.5)

                df = pd.DataFrame(index=pd.to_datetime(dt_array, unit='s', utc=True).tz_convert("Asia/Shanghai").tz_localize(None), data=np.array(diff_array), columns=["diff"])
                df["ma1"] = (df["diff"].rolling(ma_length).apply(self.polyfit_linear, raw=True) + df["diff"].rolling(short_ma_length).apply(self.polyfit_linear, raw=True) + df["diff"].rolling(long_ma_length).apply(self.polyfit_linear, raw=True)) / 3.0
                df["ma2"] = (df["diff"].rolling(ma_length).apply(self.polyfit_parabola, raw=True) + df["diff"].rolling(short_ma_length).apply(self.polyfit_parabola, raw=True) + df["diff"].rolling(long_ma_length).apply(self.polyfit_parabola, raw=True)) / 3.0
                df["slope"] = df["diff"].rolling(ma_length).apply(self.polyfit_slope, raw=True)
                df["slope_short"] = df["diff"].rolling(short_ma_length).apply(self.polyfit_slope, raw=True)
                df["slope_long"] = df["diff"].rolling(long_ma_length).apply(self.polyfit_slope, raw=True)
                df["adjust_slope"] = (df["slope"] / df["slope"].abs().rolling(ma_length).max() + df["slope_short"] / df["slope_short"].abs().rolling(short_ma_length).max() + df["slope_long"] / df["slope_long"].abs().rolling(long_ma_length).max())/3.0
                df["atr"] = df["diff"].rolling(ma_length).std()
                pr_list, cross_reg_times_long, cross_reg_times_short, cross_height_long, cross_height_short = self.calculate_price_efficiency_ratio(df, "ma1")
                df["pr_ma1"] = pr_list
                df["reg_times_long_ma1"] = cross_reg_times_long
                df["reg_times_short_ma1"] = cross_reg_times_short
                df["cross_height_long_ma1"] = cross_height_long
                df["cross_height_short_ma1"] = cross_height_short
                pr_list, cross_reg_times_long, cross_reg_times_short, cross_height_long, cross_height_short = self.calculate_price_efficiency_ratio(df, "ma2")
                df["pr_ma2"] = pr_list
                df["reg_times_long_ma2"] = cross_reg_times_long
                df["reg_times_short_ma2"] = cross_reg_times_short
                df["cross_height_long_ma2"] = cross_height_long
                df["cross_height_short_ma2"] = cross_height_short

                print(f"calculate pre grid {str(ma_length)} {key}")

                self.cta_engine.store_cache(key, df)

                return df
            else:
                return data

    def on_start(self):
        """
        Callback when strategy is started.
        """
        self.write_log("策略启动")

        self.diff_df = self.pre_caculate_grid(self.ma_length)
        self.write_log(f"计算grid完成")

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        self.write_log("策略停止")
        if self.stoploss_ratio < 0:
            setting = copy.deepcopy(self.setting)
            setting["stoploss_ratio"] = -1
            setting["grid_stoploss_ratio"] = -1
            key_setting = copy.deepcopy(setting)
            key_setting["start"] = self.cta_engine.start.strftime("%Y%m%d")
            key_setting["end"] = self.cta_engine.end.strftime("%Y%m%d")
            key_setting["trading_start"] = self.cta_engine.trading_start_date
            key_setting["symbol1"] = self.vt_symbols[0]
            key_setting["symbol2"] = self.vt_symbols[1]
            key = "spread_strategy_parabola_non_stoploss_result_" + str(key_setting)
            daily_df = copy.deepcopy(self.cta_engine.daily_df)
            if daily_df is not None:
                daily_df.index = daily_df.index.strftime("%Y%m%d").astype("int")
                daily_df["net_pnl"] = daily_df["net_pnl"].cumsum()
                daily_df["net_pnl_ma10"] = daily_df["net_pnl"].rolling(self.stoploss_ma1).mean()
                daily_df["net_pnl_ma20"] = daily_df["net_pnl"].rolling(self.stoploss_ma2).mean()
                self.cta_engine.output("计算完无止损的结果")
            self.cta_engine.store_cache(key, daily_df, expire=30000)


    def calc_grid(self, datetime: dt.datetime):
        if self.diff_df.index[0] > datetime:
            return False
        if self.diff_df_index < 0:
            rows = self.diff_df.loc[self.diff_df.index <= datetime]
            if len(rows) == 0:
                return False
            row = rows.iloc[-1]
            self.diff_df_index = len(rows) - 1
            self.diff_df_len = len(self.diff_df)
        else:
            while self.diff_df_index < (self.diff_df_len - 1) and self.diff_df.index[self.diff_df_index + 1] < datetime:
                self.diff_df_index += 1
            row = self.diff_df.iloc[self.diff_df_index]
        if self.ma_type == "linear":
            self.ma = row["ma1"]
            self.pr_ratio = row["pr_ma1"]
            self.reg_times_long = row["reg_times_long_ma1"]
            self.reg_times_short = row["reg_times_short_ma1"]
            self.cross_height_long = row['cross_height_long_ma1']
            self.cross_height_short = row["cross_height_short_ma1"]
        else:
            self.ma = row["ma2"]
            self.pr_ratio = row["pr_ma2"]
            self.reg_times_long = row["reg_times_long_ma2"]
            self.reg_times_short = row["reg_times_short_ma2"]
            self.cross_height_long = row['cross_height_long_ma2']
            self.cross_height_short = row["cross_height_short_ma2"]
        self.atr = row["atr"]
        self.slope = row["slope"]
        self.adjust_slope = row["adjust_slope"]
        if np.isnan(self.ma) or np.isnan(self.atr):
            return False

        self.long_grids[0] = self.ma - max(self.grid_levels[0] * self.atr * (self.spread_grid_std_multi + math.exp(-self.adjust_slope) -1), self.min_grid_step * self.tick)
        self.short_grids[0] = self.ma + max(self.grid_levels[0] * self.atr * (self.spread_grid_std_multi + math.exp(self.adjust_slope) -1), self.min_grid_step * self.tick)
        self.long_mids[0] = self.ma + (math.exp(self.adjust_slope) - 1) * self.atr * self.slope_adjust_ratio
        self.short_mids[0] = self.ma - (math.exp(-self.adjust_slope) - 1) * self.atr * self.slope_adjust_ratio

        return True

    def stoploss_condition(self, row, tick):
        if row["net_pnl"] < min(row["net_pnl_ma10"], row["net_pnl_ma20"]) and \
            row["net_pnl"] < (max(row["net_pnl_ma10"], row["net_pnl_ma20"]) - tick.last_price * self.cta_engine.size * self.stoploss_ratio):
            return True
        
        return False

    def on_tick(self, tick: CTickData):
        """
        Callback of new tick data update.
        """
        tick_datetime = dt.datetime.fromtimestamp(tick.timestamp)
        self.last_tick_data[tick.vt_symbol.decode()] = tick
        if dt.time(hour=5) < tick_datetime.time() < dt.time(hour=9, second=2):
            return

        if dt.time(hour=17) < tick_datetime.time() < dt.time(hour=21, second=2):
            return

        grid_calculated = False
        if self.last_grid_time is None or (tick_datetime.minute != self.last_grid_time.minute):
            if not self.calc_grid(tick_datetime):
                return
            self.last_grid_time = tick_datetime
            grid_calculated = True

        if np.isnan(self.long_grids[0]) or np.isnan(self.short_grids[0]):
            return

        if self.in_stop_loss:
            if (self.long_holds != [0] * len(self.grid_levels)) or (self.short_holds != [0] * len(self.grid_levels)):
                row = self.daily_df[self.daily_df.index < tick.trading_day].iloc[-1]
                if self.close_all():
                    self.cancel_all()
                    self.clear_grid()
                    self.write_log(f"stop loss, clear all position")
            return
        
        if grid_calculated or self.order_changed:
            # resend order
            self.order_changed = False
            for index in range(len(self.grid_levels)):
                meet_long_slope = self.pr_ratio > self.pr_ratio_threshold and self.reg_times_long > 1 and self.cross_height_long > self.atr * 0.5
                meet_short_slope = self.pr_ratio > self.pr_ratio_threshold and self.reg_times_short > 1 and self.cross_height_short < -self.atr * 0.5
                # meet_long_slope = meet_short_slope = True
                if math.isclose(self.long_holds[index], 0):
                    long_grid_price = math.floor(self.long_grids[index] /self.tick) * self.tick
                    spread_orderId = self.long_grids_open_orders[index]
                    if spread_orderId is None:
                        if math.isclose(self.short_holds[0], 0) and meet_long_slope:
                            self.long_grids_open_orders[index] = self.send_spread_order(Direction.LONG, Offset.OPEN, long_grid_price, 1, self.vt_symbols[0], self.vt_symbols[1])
                    else:
                        spread_order: SpreadOrder= self.cta_engine.spread_orders[spread_orderId]
                        if not math.isclose(self.short_holds[0], 0) or not meet_long_slope:
                            self.cancel_order(spread_orderId)
                        elif not math.isclose(spread_order.price, long_grid_price):
                            self.cancel_order(spread_orderId)
                            self.long_grids_open_orders[index] = self.send_spread_order(Direction.LONG, Offset.OPEN, long_grid_price, 1, self.vt_symbols[0], self.vt_symbols[1])
                else:
                    close_long_price = math.ceil(self.long_mids[index] / self.tick) * self.tick
                    spread_orderId = self.long_grids_close_orders[index]
                    if spread_orderId is None:
                        self.long_grids_close_orders[index] = self.send_spread_order(Direction.LONG, Offset.CLOSE, close_long_price, self.long_holds[index], self.vt_symbols[0], self.vt_symbols[1])
                    else:
                        spread_order: SpreadOrder= self.cta_engine.spread_orders[spread_orderId]
                        if not math.isclose(spread_order.price, close_long_price):
                            self.cancel_order(spread_orderId)
                            self.long_grids_close_orders[index] = self.send_spread_order(Direction.LONG, Offset.CLOSE, close_long_price, self.long_holds[index], self.vt_symbols[0], self.vt_symbols[1])
                    
        
                if math.isclose(self.short_holds[index], 0):
                    short_grid_price = math.ceil(self.short_grids[index] /self.tick) * self.tick
                    spread_orderId = self.short_grids_open_orders[index]
                    if spread_orderId is None:
                        if math.isclose(self.long_holds[0], 0) and meet_short_slope:
                            self.short_grids_open_orders[index] = self.send_spread_order(Direction.SHORT, Offset.OPEN, short_grid_price, 1, self.vt_symbols[0], self.vt_symbols[1])
                    else:
                        spread_order: SpreadOrder= self.cta_engine.spread_orders[spread_orderId]
                        if not math.isclose(self.long_holds[0], 0) or not meet_short_slope:
                            self.cancel_order(spread_orderId)
                        elif not math.isclose(spread_order.price, short_grid_price):
                            self.cancel_order(spread_orderId)
                            self.short_grids_open_orders[index] = self.send_spread_order(Direction.SHORT, Offset.OPEN, short_grid_price, 1, self.vt_symbols[0], self.vt_symbols[1])
                else:
                    close_short_price = math.floor(self.short_mids[index] / self.tick) * self.tick
                    spread_orderId = self.short_grids_close_orders[index]
                    if spread_orderId is None:
                        self.short_grids_close_orders[index] = self.send_spread_order(Direction.SHORT, Offset.CLOSE, close_short_price, self.short_holds[index], self.vt_symbols[0], self.vt_symbols[1])
                    else:
                        spread_order: SpreadOrder= self.cta_engine.spread_orders[spread_orderId]
                        if not math.isclose(spread_order.price, close_short_price):
                            self.cancel_order(spread_orderId)
                            self.short_grids_close_orders[index] = self.send_spread_order(Direction.SHORT, Offset.CLOSE, close_short_price, self.short_holds[index], self.vt_symbols[0], self.vt_symbols[1])
        
    def on_bar(self, bar: CBarData):
        """
        Callback of new bar data update.
        """
        pass

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """
        pass

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        self.put_event()

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """
        pass

    def on_spread_order(self, spread_order: SpreadOrder):
        """
        Callback of spread order update.
        """
        if spread_order.status == Status.CANCELLED:
            self.order_changed = True
            for index in range(len(self.grid_levels)):
                if self.long_grids_open_orders[index] == spread_order.spread_orderid:
                    self.long_grids_open_orders[index] = None
                    return
                elif self.long_grids_close_orders[index] == spread_order.spread_orderid:
                    self.long_grids_close_orders[index] = None
                    return
                elif self.short_grids_open_orders[index] == spread_order.spread_orderid:
                    self.short_grids_open_orders[index] = None
                    return
                elif self.short_grids_close_orders[index] == spread_order.spread_orderid:
                    self.short_grids_close_orders[index] = None
                    return
        elif spread_order.status == Status.ALLTRADED:
            self.order_changed = True
            for index in range(len(self.grid_levels)):
                if self.long_grids_open_orders[index] == spread_order.spread_orderid:
                    self.long_grids_open_orders[index] = None
                    self.long_hold_price[index] = (self.long_hold_price[index] * self.long_holds[index] + spread_order.volume * spread_order.price) / (self.long_holds[index] + spread_order.volume)
                    self.long_holds[index] += spread_order.volume
                    self.long_open_time[index] = self.cta_engine.timestamp
                    self.long_peak_price[index] = spread_order.price
                    return
                elif self.long_grids_close_orders[index] == spread_order.spread_orderid:
                    self.long_grids_close_orders[index] = None
                    self.long_holds[index] -= spread_order.volume
                    if self.long_holds[index] == 0:
                        self.long_hold_price[index] = 0
                        self.long_open_time[index] = 0
                    return
                elif self.short_grids_open_orders[index] == spread_order.spread_orderid:
                    self.short_grids_open_orders[index] = None
                    self.short_hold_price[index] = (self.short_hold_price[index] * self.short_holds[index] + spread_order.volume * spread_order.price) / (self.short_holds[index] + spread_order.volume)
                    self.short_holds[index] += spread_order.volume
                    self.short_open_time[index] = self.cta_engine.timestamp
                    self.short_peak_price[index] = spread_order.price
                    return
                elif self.short_grids_close_orders[index] == spread_order.spread_orderid:
                    self.short_grids_close_orders[index] = None
                    self.short_holds[index] -= spread_order.volume
                    if self.short_holds[index] == 0:
                        self.short_hold_price[index] = 0
                        self.short_open_time[index] = 0
                    return

    def on_new_day(self, trading_day: int):
        """
        Callback of a new trading day.
        """
        self.cancel_all()
        if self.stoploss_ratio > 0 and self.in_stop_loss:
            row = self.daily_df[self.daily_df.index < trading_day].iloc[-1]
            if row["net_pnl"] > max(row["net_pnl_ma10"], row["net_pnl_ma20"]) and row["net_pnl_ma10"] > row["net_pnl_ma20"]:
                self.in_stop_loss = False
                self.write_log(f"clear stop loss flag, start to trade on tradingday {trading_day}")
                # self.last_stoploss = None
        elif self.stoploss_ratio > 0:
            row = self.daily_df[self.daily_df.index < trading_day].iloc[-1]
            if self.vt_symbols[0] in self.last_tick_data:
                tick = self.last_tick_data[self.vt_symbols[0]]
                if self.stoploss_condition(row, tick):
                    self.in_stop_loss = True
                    self.write_log(f"stop loss")

    def clear_grid(self):
        self.long_holds = [0] * len(self.grid_levels)
        self.short_holds = [0] * len(self.grid_levels)
        self.long_hold_price = [0] * len(self.grid_levels)
        self.short_hold_price = [0] * len(self.grid_levels)
        self.long_peak_price = [-999999] * len(self.grid_levels)
        self.short_peak_price = [999999] * len(self.grid_levels)
        self.long_open_time = [None] * len(self.grid_levels)
        self.short_open_time = [None] * len(self.grid_levels)
        self.long_open_std = [None] * len(self.grid_levels)
        self.short_open_std = [None] * len(self.grid_levels)
        self.long_grids_open_orders = [None] * len(self.grid_levels)
        self.long_grids_close_orders = [None] * len(self.grid_levels)
        self.short_grids_open_orders = [None] * len(self.grid_levels)
        self.short_grids_close_orders = [None] * len(self.grid_levels)

    def on_iteration_end(self, trading_day: int):
        self.cancel_all()
        self.clear_grid()
        self.write_log(f"iteration end, clear all position")
        self.order_changed = False